package be.nepherte.movmanager.client.ui.tabframework;

import be.nepherte.movmanager.client.services.UserService;
import be.nepherte.movmanager.client.services.UserServiceAsync;
import be.nepherte.movmanager.client.ui.AddMovieTab;
import be.nepherte.movmanager.client.ui.InfoTab;
import be.nepherte.movmanager.client.ui.MyMoviesTab;
import be.nepherte.movmanager.client.ui.SearchTab;
import be.nepherte.movmanager.client.ui.components.LoginPanel;
import be.nepherte.movmanager.client.ui.tabframework.Tab;
import be.nepherte.movmanager.client.ui.tabframework.TabList;
import be.nepherte.movmanager.client.ui.tabframework.Tab.TabInfo;
import be.nepherte.movmanager.domain.UserInfo;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;


/**
 * This class holds all the tabs of the GUI. It regulates viewing the
 * different tabs and features built-in page history changes.
 * 
 * <p>
 * The main layout of the tab sink consists of a {@link TabList horizontal menu bar}
 * holding the {@link Tab tabs} in the top left corner. The top right corner of the
 * tab sink holds the {@link LoginPanel login panel}. Beneath the menu tab bar is the
 * tab description, followed by the actual tab content.
 * </p> 
 * 
 * <p>
 * Copyright © 2010 Bart Verhoeven
 * </p>
 * 
 * <p>
 * This file is part of MovManager.
 * </p>
 * 
 * <p>
 * MovManager is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * </p>
 * 
 * <p>
 * MovManager is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * </p>
 * 
 * <p>
 * You should have received a copy of the GNU General Public License along with
 * MovManager. If not, see http://www.gnu.org/licenses/.
 * </p>
 * 
 * @author	Bart Verhoeven
 * @version	0.1
 */
public class TabSink extends Composite implements ValueChangeHandler<String> {
	/**Image handler for the Tab Sink.*/
	private static final Tab.Images images = (Tab.Images) GWT.create(Tab.Images.class);
	/**User service.*/
	private final UserServiceAsync userService = (UserServiceAsync) GWT.create(UserService.class);
	/**All the tabs attached to this sink.*/
	private TabList list = new TabList(images);
	/**The info of the currently selected tab.*/
	private TabInfo curInfo;
	/**Currently selected tab.*/
	private Tab curTab;
	/**Place holder for the tab description.*/
	private Label description = new Label();
	/**Main panel holding all other panels.*/
	private VerticalPanel mainPanel = new VerticalPanel();
	/**Panel holding tab menu and login menu panels.*/
	private HorizontalPanel menuPanel = new HorizontalPanel();
	/**Top right panel with login and about info.*/
	private LoginPanel loginPanel = new LoginPanel();
	
	/**
     * When a previous history page is requested, show that page.
     */
	@Override
	public void onValueChange(ValueChangeEvent<String> event) {
		// Find the TabInfo associated with the history context. If one is
        // found, show it (It may not be found, for example, when the user 
        // mistypes a URL, or on startup, when the first context will be "").
		TabInfo tabInfo = list.find(event.getValue());
		
		if (tabInfo == null) {
			showInfoTab();
			return;
		}
		
		show(tabInfo, false);
	}
	
	/**
	 * Creates new tab sink. Loads all the tabs of the GUI and activates the
	 * Info tab.
	 */
	public TabSink() {
		loadUnauthenticatedTabs();
		loadAuthenticatedTabs();
		
		// Add everything to the panel and style/format it.
		menuPanel.add(list);
		menuPanel.add(loginPanel);
		mainPanel.add(menuPanel);
		mainPanel.add(description);
		mainPanel.setWidth("100%");
		menuPanel.setWidth("100%");
		menuPanel.setCellHorizontalAlignment(loginPanel, HorizontalPanel.ALIGN_RIGHT);
		description.setStyleName("ks-Info");
		RootPanel.get().add(mainPanel);
		
		 // Add history support
		History.addValueChangeHandler(this);
		
		// Initialize the panel widget
		initWidget(mainPanel);
		
		// Show the initial screen.
		if (History.getToken().length() > 0)
			History.fireCurrentHistoryState();
		else
			showInfoTab();
	}
	
	/**
     * Shows the info tab.
     */
	private void showInfoTab() {
		show(list.find("Info"), false);
	}
	
	/**
     * Shows a tab.
     * 
     * @param	info
     *          the info of the tab you want to show.
     * @param	affectHistory
     *          whether it should affect the browser history or not
     */
	private void show(TabInfo tabInfo, boolean affectHistory) {
		// Don't bother re-displaying the existing tab. This can be an issue
        // in practice, because when the history context is set, our
        // onHistoryChanged() handler will attempt to show the currently-visible
        // sink.
		if (tabInfo == curInfo)
			return;
		
		curInfo = tabInfo;
		
		// Remove the old tab from the display area.
		if (curTab != null) {
			curTab.onHide();
			mainPanel.remove(curTab);
		}
		
		// Get the new tab instance, and display its description in the tab list.
		curTab = tabInfo.getInstance();
		list.setTabSelection(tabInfo.getName());
		description.setText(tabInfo.getDescription());
		
		// Change the description background color.
		DOM.setStyleAttribute(description.getElement(), "backgroundColor", tabInfo.getColor());
		
		// Display the new tab.
		curTab.setVisible(false);
		mainPanel.add(curTab);
		mainPanel.setCellHorizontalAlignment(curTab, VerticalPanel.ALIGN_CENTER);
		curTab.setVisible(true);
		curTab.onShow();
	}
	
	/**
     * Adds all tabs that should be displayed at startup for which no
     * authentication info is required.
     */
	private void loadUnauthenticatedTabs() {
		list.addTab(InfoTab.init());
	}
	
	/**
	 * Adds all tabs that should be displayed at startup for which 
	 * authentication info is required.
	 */
	private void loadAuthenticatedTabs() {
		Timer t = new LoadingScreenTimer();
		AsyncCallback<UserInfo> callback = new UserServiceAsyncCallback(t);
		t.schedule(500);
		userService.requestUserInfo(GWT.getHostPageBaseURL(), callback);
	}
	
	/**
	 * Timer for loading screen.
	 */
	private class LoadingScreenTimer extends Timer {
		
		@Override
		public void run() {
			loginPanel.setLoadingScreen(true);
		}
	}
	
	/**
	 * Asynchronous callback for user service login status. 
	 */
	private class UserServiceAsyncCallback implements AsyncCallback<UserInfo> {
		
		private final Timer t;
		
		public UserServiceAsyncCallback(Timer t) {
			this.t = t;
		}
		

		@Override
		public void onFailure(Throwable caught) {
			t.cancel();
			loginPanel.setLoadingScreen(false);
			Window.alert("Could not connect to the Google Account Service");
		}

		@Override
		public void onSuccess(UserInfo result) {
			t.cancel();
			loginPanel.setLoadingScreen(false);
			loginPanel.setLoginStatus(result);
			
			if (result.isLoggedIn()) {
				list.addTab(MyMoviesTab.init(result));
				list.addTab(AddMovieTab.init(result));
				list.addTab(SearchTab.init(result));
			}
		}
	}
}
